我們今天要進入一個非常重要的主題,叫做Interrupt中斷,這對於在微處理機的設計甚至未來在OS的設計都有很大的關係,而且這是一個很重要的概念需要軟體和硬體配合,他才能做接下來這樣的工作。
Interrupts中文意思是中斷,所以一定跟事情被中斷、打斷有關嘛。
我舉一個例子吧,當你正在看電視,突然電話響起,於是你把手邊事情放下,接起電話,事情處理完然後回來繼續工作,這就是中斷(interrupts),而中斷源就是電話響起。
微控制器的 CPU 遇到中斷時,也會停下手邊工作,到指定的位置(接電話)執行一段程式,執行完之後再回來處理剛剛的工作。
很重要,當CPU發生中斷通常會做這幾個動作:
中斷的定義:
中斷是指電腦在執行某一程式的過程中,由於電腦系統內、外的某種原因,而必須終止原程式的執行,轉去執行相應的處理程式,待處理結束之後,再回來繼續執行被終止的原程式過程。
想像你正在用電腦,可能一邊打著報告,一邊查資料,背景還在放音樂,是同時好多事情在處理。過去我們學習的方式都是用輪詢,如果用輪詢的方式同時進行這些程式可能會相當的複雜。所以我們需要認識中斷,那它可以幫助我們在同時多工的處理上,會相對起來必較方便。
為甚麼我們需要中斷,我用一個簡單的例子解釋原因,在STM32L0 開發板上,有兩個LED和兩個按鈕。
假設我們要開發一個專案,如果我們按下左邊藍色按鈕,程式會點亮綠色LED,在之前的文章中,我們了解了如何控制GPIO的輸出以控制IO的High、Low變化,以及GPIO如何輸入以讀取IO的邏輯狀態。
有兩種方式我們能input按鈕目前的邏輯狀態,一種是輪詢(Polling),一種是中斷(Interrupts)。輪詢的方法就像你每隔幾秒鐘拿起電話來檢查你是否正在接到電話。中斷方法就像等待電話響起。哪種方法更加有效呢?毫無疑問的,是中斷方法更合適。你可以自由地做自己的事情,只有在電話響起時接起電話,
//輪詢(Polling)方法
while(1){
read_button_input;
if(pushed)
exit;
}
這是一個例子將輪詢寫成程式,在while迴圈中,程式會不斷讀取按鈕的輸入,直到按鈕被按下,程式才離開while迴圈。接著點亮LED。輪詢的方法就是CPU不斷的去問,問你的按鈕輸入了嗎?問是否正在接到電話呢?直到按鈕被按下,LED才被點亮。顯而易見,輪詢的方法簡單但沒有效率。
//中斷(Interrupts)方法
interrupt_requestion(){
turn_on_led;
exit;
}
中斷比輪詢顯得有效率的多了,當按下按鈕,則會產生一個脈衝訊號,稱為中斷請求,CPU收到中斷請求後,將自動暫停執行原本執行的程式,並開始執行中斷的程式,中斷程序完成之後,處理器回到剛才被打斷的地方,繼續執行主程式,
在微控制器中,中斷的來源有很多,諸如 Reset, 外部中斷, Timer溢出可以中斷, USART接收傳輸資料可以中斷, ADC轉換完成可以中斷...等。
下面是Interrupts、Polling範例程式碼,使用輪詢和中斷當按鈕被按下,GreenLED點亮。
//輪詢(Polling)方法
#include "main.h"
#include "stm32l0xx_hal.h"
#define turn_on_LED HAL_GPIO_WritePin( GPIOA, GPIO_PIN_5, GPIO_PIN_SET)
uint8_t btn,pushed = 0;
uint8_t read_button_input(void){
btn <<= 1;
btn += HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13);
btn &= 3;
if(btn==2){ return 1;}
return 0;
}
int main(void){
while(1){
pushed = read_button_input();
if(pushed)
exit;
}
turn_on_LED;
}
//中斷(Interrupts)方法
#include "main.h"
#include "stm32l0xx_hal.h"
#define turn_on_LED HAL_GPIO_WritePin( GPIOA, GPIO_PIN_5, GPIO_PIN_SET)
uint8_t btn,pushed = 0;
uint8_t read_button_input(void){
btn <<= 1;
btn += HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13);
btn &= 3;
if(btn==2){ return 1;}
return 0;
}
extern TIM_HandleTypeDef htim6;
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){
pushed = read_button_input();
if(pushed) turn_on_LED;
}
int main(void){
HAL_TIM_Base_Start_IT(&htim6);
while(1){
}
}
- STM32 Nucleo-64 boards (MB1136)資料手冊
https://www.st.com/resource/en/user_manual/dm00105823-stm32-nucleo-64-boards-mb1136-stmicroelectronics.pdf- STM32L053R8 datasheet
https://www.st.com/resource/en/datasheet/stm32l053r8.pdf- RM0367 Reference manual Ultra-low-power STM32L0x3 advanced Arm®-based 32-bit MCUs
https://www.st.com/resource/en/reference_manual/dm00095744-ultra-low-power-stm32l0x3-advanced-arm-based-32-bit-mcus-stmicroelectronics.pdf- ARM Mbed官網介紹 - NUCLEO-L053R8
https://os.mbed.com/platforms/ST-Nucleo-L053R8/- STM32 官網介紹 - STM32L053R8
https://www.st.com/en/microcontrollers-microprocessors/stm32l053r8.html#overview
- 此開發板售價在臺幣1000元以下,讀者可於以下網站購得:
https://www.mouser.tw/
https://www.digikey.tw/
今天到此謝謝大家。
請問read_button_input裡面做的事是什麼?
為何不用 HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13)判斷button狀態就好?
感謝